/*
    作者：zx
    时间：2022.12.1
    描述：虚拟内存管理单元
*/

#ifndef MMU_H
#define MMU_H

#include "comm/types.h"
#include "comm/cpu_instr.h"

#define PAGE_DIRECTORY_LENGTH 1024
#define PAGE_TABLE_LENGTH 1024
#define PTE_P (1<<0)
#define PDE_P (1<<0)

#define PTE_W (1<<1)
#define PDE_W (1<<1)

#define PTE_R (0<<1)
#define PDE_R (0<<1)

#define PTE_U (1<<2)
#define PDE_U (1<<2)

// 页目录表结构/一级页表 页表项定义
// 采用共用体，也从不同长度查看内存 一共32位
// 具体查看intel手册 table 4-5
typedef union
{
    // 直接查看32位
    uint32_t v;
    struct
    {
        // 是否存在
        uint32_t persent : 1;
        // 是否可写
        uint32_t write_enable : 1;
        // 用户模式下能否访问
        uint32_t user_mode_access : 1;
        // 对应的二级页表内存类型？
        uint32_t write_through : 1;
        // 对应的二级页表缓存类型？
        uint32_t cache_disable : 1;
        // 是否用于逻辑地址转换
        uint32_t accessed : 1;
        // 省略位
        uint32_t : 1;
        // 二级页表大小 为1则页面大小为4MB,0则4KB
        uint32_t ps : 1;
        // 省略位
        uint32_t : 4;
        // 对应的4-KB对齐页表的物理地址
        uint32_t physical_page_table_addr :20;
    };
    
}page_directory_entry_t;


// 二级页表 页表项定义
// 采用共用体，也从不同长度查看内存 一共32位
// 具体查看intel手册 table 4-6
typedef union
{
    // 直接查看32位
    uint32_t v;
    struct
    {
        // 是否存在
        uint32_t persent : 1;
        // 是否可写
        uint32_t write_enable : 1;
        // 用户模式下能否访问
        uint32_t user_mode_access : 1;
        // 对应的二级页表内存类型？
        uint32_t write_through : 1;
        // 对应的二级页表缓存类型？
        uint32_t cache_disable : 1;
        // 是否用于逻辑地址转换
        uint32_t accessed : 1;
        // 软件是否已写入此条目引用的4KB页面
        uint32_t dirty: 1;
        // 如果PAT受支持，则间接确定用于访问此引用的4 KB页的内存类型
        uint32_t pat : 1;
        // if CR4.PGE = 1, determines whether the translation is global; ignored otherwise
        uint32_t global: 1;
        // 省略位
        uint32_t : 3;
        // 对应的4KB的物理地址
        uint32_t physical_page_addr :20;
    };
    
}page_table_entry_t;

// 设置页目录表到cr3寄存器
static inline void mmu_set_page_dir(uint32_t page_dir_addr)
{
    write_cr3(page_dir_addr);
}

// 获取线性地址对应的一级页表表项下标，高10位为下标
static inline int index_in_pde(uint32_t linear_addr)
{
    return linear_addr >> 22;
}

static inline uint32_t index_in_pte(uint32_t linear_addr)
{
    return (linear_addr>>12) & 0x3FF;
}  

static inline uint32_t pte_addr_in_pde(page_directory_entry_t * pde)
{
    return (pde->physical_page_table_addr << 12);
}  

static inline uint32_t page_addr_in_pte(page_table_entry_t * pte)
{
    return (pte->physical_page_addr << 12);
}  


static inline uint32_t get_pte_perm(page_table_entry_t * pte)
{
    return (pte->v & 0x3FF);
}
#endif // !MMU_H